<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Project-Search</title>
  <!-- Bootstrap CSS -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
  <!-- Vue.js -->
  <script src="https://cdn.jsdelivr.net/npm/vue@3.2.47/dist/vue.global.prod.js"></script>
  <!-- Axios for API calls -->
  <script src="https://cdn.jsdelivr.net/npm/axios@1.4.0/dist/axios.min.js"></script>
  <!-- Marked for Markdown parsing -->
  <script src="https://cdn.jsdelivr.net/npm/marked@5.0.2/marked.min.js"></script>
  <!-- Highlight.js for syntax highlighting -->
  <script src="
  https://cdn.jsdelivr.net/npm/highlightjs@9.16.2/highlight.pack.min.js
  "></script>
  <link href="
  https://cdn.jsdelivr.net/npm/highlightjs@9.16.2/styles/monokai-sublime.min.css
  " rel="stylesheet">

  <style>
    body {
      background-color: #f5f5f5;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
      overflow: hidden;
    }
    .sidebar {
      height: 100vh;
      width: 280px;
      background-color: #202123;
      color: white;
      position: fixed;
      top: 0;
      left: 0;
      overflow-y: auto;
      padding: 20px;
      transition: width 0.3s;
      z-index: 100;
    }
    .sidebar.collapsed {
      width: 60px;
    }
    .sidebar.collapsed .sidebar-content {
      display: none;
    }
    .history-item {
      position: relative;
      padding: 12px;
      margin-bottom: 8px;
      border-radius: 5px;
      cursor: pointer;
      background-color: #343541;
      transition: background-color 0.2s ease;
    }
    .history-item:hover {
      background-color: #444654;
    }
    .history-item:hover .dropdown-toggle {
      display: block;
    }
    .dropdown-toggle {
      display: none;
      position: absolute;
      right: 10px;
      top: 50%;
      transform: translateY(-50%);
      background: none;
      border: none;
      color: white;
      font-size: 20px;
      cursor: pointer;
    }
    .history-item .dropdown-menu {
      position: absolute;
      right: -80px;
      top: 30px;
      background-color: #444654;
      border-radius: 5px;
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
      z-index: 1000;
      overflow: visible;
      display: block; /* Ensure the menu is displayed */
    }
    .history-item .dropdown-item {
      padding: 10px 15px;
      color: white;
      text-decoration: none;
      display: flex;
      align-items: center;
      background-color: transparent;
    }
    .history-item .dropdown-item i {
      margin-right: 8px;
    }
    .history-item .dropdown-item:hover {
      background-color: #555;
    }
    .history-summary {
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      font-weight: 500;
    }
    .history-time {
      font-size: 12px;
      color: #aaa;
      margin-top: 4px;
    }
    .chat-container {
      margin-left: 280px;
      width: calc(80% - 300px);
      margin-right: auto;
      padding: 20px 30px;
      height: calc(100vh - 120px);
      overflow-y: auto;
      box-sizing: border-box;
    }
    .message {
      margin: 20px 0;
      display: flex;
      align-items: flex-start;
    }
    .message.user {
      justify-content: flex-end;
    }
    .message-content {
      max-width: 85%;
      padding: 15px 20px;
      border-radius: 12px;
      line-height: 1.6;
      position: relative;
      font-size: 16px;
    }
    .user .message-content {
      background-color: #007bff;
      color: white;
    }
    .bot .message-content {
      background-color: #ffffff;
      color: #333;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    }
    .input-container {
      position: fixed;
      bottom: 25px;
      left: 280px;
      width: calc(80% - 300px);
      padding: 0 30px;
    }
    .input-group {
      background-color: #ffffff;
      border: 1px solid #ddd;
      border-radius: 12px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
      padding: 5px;
    }
    .input-group input {
      border: none;
      box-shadow: none;
      font-size: 16px;
      padding: 12px;
    }
    .input-group input:focus {
      box-shadow: none;
    }
    .toggle-btn {
      background: none;
      border: none;
      color: white;
      font-size: 20px;
      cursor: pointer;
    }
    .mic-btn {
      transition: background-color 0.2s;
      border-radius: 8px;
      padding: 10px 15px;
    }
    .mic-btn.recording {
      background-color: #dc3545 !important;
    }
    .play-btn {
      margin-left: 10px;
      font-size: 14px;
    }
    
    /* Markdown样式 */
    .markdown-content {
      overflow-x: auto;
    }
    .markdown-content pre {
      background-color: #f6f8fa;
      border-radius: 6px;
      padding: 16px;
      overflow-x: auto;
    }
    .markdown-content code {
      font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
      font-size: 14px;
      padding: 2px 4px;
      border-radius: 3px;
    }
    .markdown-content p {
      margin-bottom: 16px;
    }
    .markdown-content h1, .markdown-content h2, .markdown-content h3, 
    .markdown-content h4, .markdown-content h5, .markdown-content h6 {
      margin-top: 24px;
      margin-bottom: 16px;
      font-weight: 600;
      line-height: 1.25;
    }
    .markdown-content table {
      border-collapse: collapse;
      width: 100%;
      margin-bottom: 16px;
    }
    .markdown-content table th, .markdown-content table td {
      padding: 8px;
      border: 1px solid #ddd;
    }
    .markdown-content table th {
      background-color: #f6f8fa;
      font-weight: 600;
    }
    .markdown-content blockquote {
      padding: 0 1em;
      color: #6a737d;
      border-left: 0.25em solid #dfe2e5;
      margin-bottom: 16px;
    }
    
    /* 用户消息中的Markdown样式调整 */
    .user .markdown-content {
      color: white;
    }
    .user .markdown-content pre, .user .markdown-content code {
      background-color: rgba(255, 255, 255, 0.1);
      color: #f8f9fa;
    }
    .user .markdown-content blockquote {
      color: #f8f9fa;
      border-left-color: rgba(255, 255, 255, 0.5);
    }

    /* 响应式设计 */
    @media (max-width: 992px) {
      .sidebar {
        width: 240px;
      }
      .sidebar.collapsed {
        width: 50px;
      }
      .chat-container {
        margin-left: 240px;
        width: calc(100% - 260px);
      }
      .input-container {
        left: 240px;
        width: calc(100% - 260px);
      }
    }

    @media (max-width: 768px) {
      .sidebar:not(.collapsed) {
        width: 100%;
      }
      .chat-container {
        margin-left: 50px;
        width: calc(100% - 50px);
        padding: 15px;
      }
      .input-container {
        left: 50px;
        width: calc(100% - 50px);
        padding: 0 15px;
        bottom: 15px;
      }
      .message-content {
        max-width: 90%;
      }
    }
  </style>
</head>
<body>
  <div id="app">
    <!-- 侧边栏 -->
    <div class="sidebar" :class="{ collapsed: isSidebarCollapsed }">
      <div class="d-flex justify-content-between align-items-center mb-3">
        <button class="toggle-btn" @click="toggleSidebar">
            {{ isSidebarCollapsed ? '>>' : '<<' }}
        </button>
        <div class="d-flex align-items-center">
          <h5 v-if="!isSidebarCollapsed" class="m-0">历史对话</h5>
        </div>
        <button v-if="!isSidebarCollapsed" class="btn btn-sm btn-outline-light " title="新聊天" @click="startNewChat">
            <i class="bi bi-plus-circle"></i>
          </button>
      </div>
      <div class="sidebar-content">
        <div v-for="session in chatHistory" :key="session.id" class="history-item" @click="loadSession(session.id)">
          <div class="history-summary">{{ session.summary }}</div>
          <div class="history-time">{{ formatTime(session.updated_at) }}</div>
          <!-- Hidden button for dropdown menu -->
          <button class="dropdown-toggle" @click.stop="toggleDropdown(session.id)">
            <i class="bi bi-three-dots"></i>
          </button>
          <!-- Dropdown menu -->
          <div v-if="session.showDropdown" class="dropdown-menu">
            <a class="dropdown-item" href="#" @click.stop="exportSession(session.id)">
              <i class="bi bi-box-arrow-up"></i> 导出
            </a>
            <a class="dropdown-item" href="#" @click.stop="deleteSession(session.id)">
              <i class="bi bi-trash"></i> 删除
            </a>
            <a class="dropdown-item" href="#" @click.stop="renameSession(session.id)">
              <i class="bi bi-pencil"></i> 重命名
            </a>
          </div>
        </div>
        <div v-if="chatHistory.length === 0" class="text-center text-muted mt-3">
          暂无历史对话记录
        </div>
        <a href="mcp.html" class="btn btn-outline-light mt-3 w-100">MCP Servers</a>
        <div class="form-check form-switch mt-3 text-white">
          <input class="form-check-input" type="checkbox" id="autoSpeechSwitch" v-model="autoSpeech">
          <label class="form-check-label" for="autoSpeechSwitch">自动语音播放</label>
        </div>
        <div class="form-check form-switch mt-2 text-white">
          <input class="form-check-input" type="checkbox" id="webSearchSwitch" v-model="webSearch">
          <label class="form-check-label" for="webSearchSwitch">联网搜索</label>
        </div>
      <!--  参考联网搜索设置，增加启用agent的开关 -->
        <div class="form-check form-switch mt-2 text-white">
          <input class="form-check-input" type="checkbox" id="agentModeSwitch" v-model="agentMode">
          <label class="form-check-label" for="agentModeSwitch">启用Agent</label>
        </div>
          
      </div>
    </div>
    <!-- 对话区域 -->
    <div class="chat-container" ref="chatContainer">
      <div v-for="(msg, index) in messages" :key="index" class="message" :class="{ user: msg.role === 'user', bot: msg.role === 'bot' }">
        <div class="message-content">
          <div v-if="msg.role === 'user'" v-html="msg.content"></div>
          <div v-else class="markdown-content" v-html="renderMarkdown(msg.content)"></div>
          <button v-if="msg.role === 'bot' && msg.content" class="btn btn-sm btn-outline-secondary play-btn" @click="toggleSpeech(msg.content, index)">
            {{ speakingIndex === index ? '停止' : '播放' }}
          </button>
        </div>
      </div>
    </div>
    <!-- 输入框 -->
    <div class="input-container">
      <div class="input-group">
        <input v-model="userInput" @keyup.enter="sendMessage" type="text" class="form-control" placeholder="输入您的问题或点击麦克风...">
        <button class="btn btn-outline-secondary mic-btn" :class="{ recording: isRecording }" @click="toggleRecording">
          <i class="bi bi-mic-fill"></i>
        </button>
        <button v-if="webSearch" class="btn btn-outline-info" title="当前启用联网搜索">
          <i class="bi bi-globe"></i>
        </button>
        <button v-if="agentMode" class="btn btn-outline-info" title="当前启用Agent">
          <i class="bi bi-robot"></i>
        </button>
     
        
        <button class="btn btn-primary rounded-pill px-4" @click="sendMessage">发送</button>
      </div>
    </div>
  </div>

  <script>
    const { createApp } = Vue;
    
    // 设置API基础URL - 确保与服务器地址匹配
    const API_BASE_URL = 'https://www.en9.cn';
    
    // 全局设置 axios 的 baseURL
    axios.defaults.baseURL = API_BASE_URL;
    
    // 配置marked选项
    marked.setOptions({
      highlight: function(code, lang) {
        const language = hljs.getLanguage(lang) ? lang : 'plaintext';
        return hljs.highlight(code, { language }).value;
      },
      langPrefix: 'hljs language-',
      gfm: true,
      breaks: true
    });
    
    createApp({
      data() {
        return {
          userInput: '',
          messages: [],
          chatHistory: [],
          isSidebarCollapsed: false,
          isRecording: false,
          recognition: null,
          speechSynthesis: window.speechSynthesis,
          speakingIndex: null, // 当前播放的消息索引
          isServerConnected: false,
          autoSpeech: false, // 默认启用自动语音播放
          currentUtterance: null, // 当前语音合成对象
          currentSessionId: null, // 当前会话ID
          webSearch: false, // 联网搜索开关，默认关闭
          agentMode: false, // Agent开关，默认关闭
        };
      },
      mounted() {
        this.fetchChatHistory();
        this.initSpeechRecognition();
        this.testServerConnection();
        document.addEventListener('click', this.handleClickOutside);
      },
      beforeUnmount() {
        document.removeEventListener('click', this.handleClickOutside);
      },
      methods: {
        renderMarkdown(text) {
          if (!text) return '';
          try {
            // 处理可能的HTML实体
            const decoded = text.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
            return marked.parse(decoded);
          } catch (error) {
            console.error('Markdown解析错误:', error);
            return text;
          }
        },
        initSpeechRecognition() {
          const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
          if (SpeechRecognition) {
            this.recognition = new SpeechRecognition();
            this.recognition.lang = 'zh-CN';
            this.recognition.interimResults = true;
            this.recognition.continuous = true;  // 改为 true，持续识别

            this.recognition.onresult = (event) => {
              // 获取最新的识别结果
              const lastResultIndex = event.results.length - 1;
              const transcript = event.results[lastResultIndex][0].transcript;
              this.userInput = transcript;
              // 已注释掉自动发送逻辑
            };

            this.recognition.onerror = (event) => {
              console.error('语音识别错误:', event.error);
              this.isRecording = false;
              alert('语音识别失败，请检查麦克风或浏览器支持！');
            };

            this.recognition.onend = () => {
              // 只有当用户手动停止时，才会通过 toggleRecording 设置 isRecording = false
              // 这里不做任何操作，防止自动停止识别
              if (this.isRecording) {
                this.recognition.start();  // 自动重新开始识别
              }
            };
          } else {
            alert('您的浏览器不支持语音识别功能！');
          }
        },
        toggleRecording() {
          if (!this.recognition) return;

          if (this.isRecording) {
            this.recognition.stop();
            this.isRecording = false;
          } else {
            this.userInput = '';
            this.recognition.start();
            this.isRecording = true;
          }
        },
        toggleSpeech(text, index) {
          if (this.speakingIndex === index) {
            this.speechSynthesis.cancel();
            this.speakingIndex = null;
            this.currentUtterance = null;
          } else {
            this.speechSynthesis.cancel();
            this.speakText(text, index);
          }
        },
        
        speakText(text, index) {
          const utterance = new SpeechSynthesisUtterance(text);
          utterance.lang = 'zh-CN';
          utterance.onend = () => {
            this.speakingIndex = null;
            this.currentUtterance = null;
          };
          this.speechSynthesis.speak(utterance);
          this.speakingIndex = index;
          this.currentUtterance = utterance;
        },
        
        async fetchChatHistory() {
          try {
            const response = await axios.get('api/chat/history');
            this.chatHistory = response.data.map(session => ({
              ...session,
              showDropdown: false // Initialize showDropdown to false
            }));
          } catch (error) {
            console.error('获取历史对话失败:', error);
            alert('获取历史对话失败！');
          }
        },
        async loadSession(sessionId) {
          try {
            const response = await axios.get(`api/chat/session/${sessionId}`);
            this.messages = response.data.messages;
            this.currentSessionId = sessionId; // 设置当前会话ID
            this.$nextTick(() => {
              this.$refs.chatContainer.scrollTop = this.$refs.chatContainer.scrollHeight;
            });
          } catch (error) {
            console.error('加载对话失败:', error);
            alert('加载对话失败！');
          }
        },
        async sendMessage() {
          if (!this.userInput.trim()) return;

          const userMessage = this.userInput;
          this.messages.push({ role: 'user', content: userMessage });
          this.userInput = '';
          this.messages.push({ role: 'bot', content: '正在思考...' });
          
          try {
            const botMessageIndex = this.messages.length - 1;
            
            // 清除"正在思考..."提示
            this.messages[botMessageIndex].content = '';
            
            // 重置语音相关状态
            this.speechSynthesis.cancel();
            this.currentUtterance = null;
            
            // 构建API URL，如果有会话ID则添加到查询参数中
            let apiUrl = `${API_BASE_URL}/api/stream?query=${encodeURIComponent(userMessage)}`;
            if (this.currentSessionId) {
              apiUrl += `&session_id=${this.currentSessionId}`;
            }
            
            // 添加联网搜索参数
            if (this.webSearch) {
              apiUrl += `&web_search=true`;
            }

            // 添加Agent参数
            if (this.agentMode) {
              apiUrl += `&agent_mode=true`;
            }
            
            // 使用 EventSource API 处理 SSE
            const eventSource = new EventSource(apiUrl);
            
            eventSource.onmessage = (event) => {
              try {
                const data = JSON.parse(event.data);
                
                // 检查是否收到完成信号
                if (data.done) {
                  eventSource.close();
                  
                  // 保存会话ID
                  if (data.session_id) {
                    this.currentSessionId = data.session_id;
                    console.log("会话ID:", this.currentSessionId);
                  }
                  
                  // 当收到完整响应后再播放语音
                  if (this.autoSpeech) {
                    const botMessage = this.messages[botMessageIndex].content;
                    if (botMessage && botMessage.trim()) {
                      this.speakText(botMessage, botMessageIndex);
                    }
                  }
                  
                  // 当消息完成时，应用代码高亮
                  this.$nextTick(() => {
                    document.querySelectorAll('pre code').forEach((block) => {
                      hljs.highlightElement(block);
                    });
                  });
                  
                  // 更新聊天历史
                  this.fetchChatHistory();
                  
                  return;
                }
                
                // 处理常规消息
                if (data.content) {
                  this.messages[botMessageIndex].content += data.content;
                  console.log("收到内容块:", data.content);
                  
                  // 如果收到会话ID但尚未设置，则保存它
                  if (data.session_id && !this.currentSessionId) {
                    this.currentSessionId = data.session_id;
                  }
                  
                  // 滚动到底部
                  this.$nextTick(() => {
                    this.$refs.chatContainer.scrollTop = this.$refs.chatContainer.scrollHeight;
                  });
                }
              } catch (e) {
                console.error('解析响应数据失败:', e, event.data);
              }
            };
            
            eventSource.onerror = (error) => {
              console.error('SSE错误:', error);
              eventSource.close();
              
              // 如果没有收到内容
              if (!this.messages[botMessageIndex].content) {
                this.messages[botMessageIndex].content = '服务器连接错误，请稍后再试';
              }
            };
            
          } catch (error) {
            console.error('发送消息错误:', error);
            this.messages[this.messages.length - 1].content = `错误: ${error.message}`;
          }
        },
        toggleSidebar() {
          this.isSidebarCollapsed = !this.isSidebarCollapsed;
        },
        startNewChat() {
          // 清空当前对话
          this.messages = [];
          // 清除当前会话ID
          this.currentSessionId = null;
          // 关闭当前正在播放的语音
          this.speechSynthesis.cancel();
          this.speakingIndex = null;
        },
        async testServerConnection() {
          try {
            console.log('测试服务器连接...');
            const response = await fetch(`${API_BASE_URL}/api/health`);
            
            if (response.ok) {
              console.log('服务器连接正常:', await response.json());
              this.isServerConnected = true;
              this.messages.push({ 
                role: 'bot', 
                content: '服务器连接正常，您可以开始对话。'
              });
              
              // 自动播放服务器连接成功的提示
              if (this.autoSpeech) {
                this.$nextTick(() => {
                  const index = this.messages.length - 1;
                  // 使用常规播放方式，因为这不是流式输出
                  this.speakText(this.messages[index].content, index);
                });
              }
            } else {
              console.error('服务器连接异常:', response.status);
              this.messages.push({ 
                role: 'bot', 
                content: `服务器连接异常(${response.status})，请检查服务器是否启动。`
              });
            }
          } catch (error) {
            console.error('无法连接到服务器:', error);
            this.messages.push({ 
              role: 'bot', 
              content: `无法连接到服务器: ${error.message}。请确保服务器已启动且地址正确。`
            });
          }
        },
        formatTime(timestamp) {
          if (!timestamp) return '';
          
          const date = new Date(timestamp.replace(/-/g, '/'));
          const now = new Date();
          const diffDays = Math.floor((now - date) / (24 * 60 * 60 * 1000));
          
          if (diffDays === 0) {
            // 今天 - 显示时间
            return '今天 ' + date.toLocaleTimeString('zh-CN', {hour: '2-digit', minute:'2-digit'});
          } else if (diffDays === 1) {
            // 昨天
            return '昨天 ' + date.toLocaleTimeString('zh-CN', {hour: '2-digit', minute:'2-digit'});
          } else if (diffDays < 7) {
            // 一周内 - 显示星期几
            const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
            return '星期' + weekdays[date.getDay()];
          } else {
            // 更早 - 显示完整日期
            return date.toLocaleDateString('zh-CN');
          }
        },
        handleClickOutside(event) {
          const dropdowns = document.querySelectorAll('.dropdown-menu');
          let isClickInside = false;
          dropdowns.forEach(dropdown => {
            if (dropdown.contains(event.target)) {
              isClickInside = true;
            }
          });
          if (!isClickInside) {
            this.chatHistory = this.chatHistory.map(session => ({
              ...session,
              showDropdown: false
            }));
          }
        },
        toggleDropdown(sessionId) {
          this.chatHistory = this.chatHistory.map(session => {
            if (session.id === sessionId) {
              session.showDropdown = !session.showDropdown;
            } else {
              session.showDropdown = false;
            }
            return session;
          });
        },
        
        exportSession(sessionId) {
          try {
            const link = document.createElement('a');
            link.href = `${API_BASE_URL}/api/chat/export/${sessionId}`;
            link.download = `session_${sessionId}.md`; // 设置下载文件名
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
          } catch (error) {
            console.error('导出会话失败:', error);
            alert('导出会话失败！');
          }
        },
    
        deleteSession(sessionId) {
          if (confirm('Are you sure you want to delete this session?')) {
            this.chatHistory = this.chatHistory.filter(session => session.id !== sessionId);
            try {
               axios.delete(`/api/chat/session/${sessionId}`);
            } catch (error) {
              console.error('删除服务器失败:', error);
              alert('删除服务器失败！');
            }
          }
        },
        renameSession(sessionId) {
          const newName = prompt('Enter new name for the session:');
          if (newName) {
            const session = this.chatHistory.find(session => session.id === sessionId);
            if (session) {
              session.summary = newName;
              alert(`Session ${sessionId} renamed to ${newName}`);
              // Implement rename logic here
            }
          }
        }
      },
    }).mount('#app');
  </script>

  <!-- Bootstrap JS -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
  <!-- Bootstrap Icons for mic icon -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
</body>
</html>